package com.caigua.mgp.mg.utils;

import com.intellij.openapi.ui.Messages;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ShellCallback;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.InputStream;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @BelongsProject: mybatis_generator_pluing
 * @BelongsPackage: com.caigua.mgp.mg.utils
 * @Author: caigua
 * @CreateTime: 2020-12-09 00:34
 * @Description: todo
 */
public final class GenUtil {

    private static GenUtil instance;

    private static Configuration configuration;

    private static final List<String> warnings = new ArrayList<>();

    public static List<TableInfo> cacheTables = new ArrayList<>();

    public static StringBuilder pageParamClass = new StringBuilder("com.caigua.PageRecord");

    public static StringBuilder domainOverPrefix = new StringBuilder();

    private GenUtil() {}

    static {
        try {
            readConfiguration();
        } catch (Throwable e) {
            // ignore
            e.printStackTrace();
        }
    }

    public static void setPageParamClass(String classUrl) {
        if(StringUtils.isNotBlank(classUrl)) {
            pageParamClass.setLength(0);
            pageParamClass.append(classUrl);
        }
    }

    public static String getPageParamClassName() {
        return pageParamClass.toString().substring(pageParamClass.toString().lastIndexOf(".") + 1);
    }

    private static void readConfiguration() throws Throwable {

        Configuration configuration = new Configuration();
        Context context = new Context(null);
        configuration.addContext(context);

        context.setId("mysqlTables");

        JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();
        jdbcConnectionConfiguration.setDriverClass("com.mysql.cj.jdbc.Driver");
        jdbcConnectionConfiguration.addProperty("nullCatalogMeansCurrent", "true");
        context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);


        JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
        javaTypeResolverConfiguration.setConfigurationType("com.caigua.mgp.mg.internal.CustormizeJavaTypeResolverDefaultImpl");
        javaTypeResolverConfiguration.addProperty("forceBigDecimals", "false");
        context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);

        SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();
        sqlMapGeneratorConfiguration.addProperty("enableSubPackages",  "true");
        context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);

        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();
        javaModelGeneratorConfiguration.addProperty("enableSubPackages","true");
        javaModelGeneratorConfiguration.addProperty("trimStrings","true");
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

        JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();
        javaClientGeneratorConfiguration.setConfigurationType("CUSTOMIZE");
        javaClientGeneratorConfiguration.addProperty("enableSubPackages","true");
        context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

        CommentGeneratorConfiguration commentGeneratorConfiguration = new CommentGeneratorConfiguration();
        commentGeneratorConfiguration.setConfigurationType("com.caigua.mgp.mg.internal.CustomizeCommentGenerator");
        commentGeneratorConfiguration.addProperty("suppressDate", "false");
        commentGeneratorConfiguration.addProperty("suppressAllComments", "false");
        context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);

        context.setTargetRuntime("com.caigua.mgp.mg.internal.CustomizeIntrospectedTableMyBatis3Impl");

        context.addProperty("xmlFormatter", "org.mybatis.generator.api.dom.DefaultXmlFormatter");
        context.addProperty("javaFormatter", "org.mybatis.generator.api.dom.DefaultJavaFormatter");
        context.addProperty("javaFileEncoding", "UTF-8");
        GenUtil.configuration = configuration;
    }

    private static TableConfiguration newTableInfo(String tableName, String domainObjectName) {
        TableConfiguration tableConfiguration = new TableConfiguration(getContext());
        tableConfiguration.setSelectByExampleStatementEnabled(false);
        tableConfiguration.setUpdateByExampleStatementEnabled(false);
        tableConfiguration.setCountByExampleStatementEnabled(false);
        tableConfiguration.setDeleteByExampleStatementEnabled(false);
        GeneratedKey generatedKey = new GeneratedKey("id", "JDBC", false, null);
        tableConfiguration.setGeneratedKey(generatedKey);
        tableConfiguration.setSelectByExampleQueryId("false");
        tableConfiguration.setSchema("root");
        tableConfiguration.setTableName(tableName);
        tableConfiguration.setDomainObjectName(domainObjectName);
        tableConfiguration.setWildcardEscapingEnabled(false);
        tableConfiguration.setDelimitIdentifiers(false);
        tableConfiguration.setAllColumnDelimitingEnabled(false);
        return tableConfiguration;
    }

    public static void addTableInfo(String tableName, String domainObjectName) {
        getContext().addTableConfiguration(newTableInfo(tableName, domainObjectName));
    }

    public static GenUtil instance() {
        return Optional.ofNullable(instance).orElseGet(() -> {
            instance = new GenUtil();
            return instance;
        });
    }

    public static void generateCode() throws Throwable {
        DefaultShellCallback callback = new DefaultShellCallback(true);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(configuration, (ShellCallback)callback, warnings);
        myBatisGenerator.generate(null);
    }


    public static void main(String[] args) {
       /* try {
            GenUtil.setDBInfo("jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useSSL=false", "root", "");
            GenUtil.setJavaModelConfig("", "com.caigua.entity", "src/main/java");
            GenUtil.setXMLMapperConfig("", "mappers/", "src/main/resources");
            GenUtil.setJavaMapperConfig("", "com.caigua.mapper", "src/main/java");
            GenUtil.addTableInfo("persion", "Persion");
            generateCode();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }*/
        System.out.println(GenUtil.getPageParamClassName());
    }

    public static Configuration getConfiguration() {
        return configuration;
    }

    public static void setDBInfo(String jdbcUrl, String user, String password) {
        getContext().getJdbcConnectionConfiguration().setConnectionURL(jdbcUrl);
        getContext().getJdbcConnectionConfiguration().setPassword(password);
        getContext().getJdbcConnectionConfiguration().setUserId(user);
    }

    public static void setJavaModelConfig(String model, String targetPackage, String targetProject) {
        getContext().getJavaModelGeneratorConfiguration().setTargetPackage(targetPackage);
        if(StringUtils.isBlank(model)) {
            getContext().getJavaModelGeneratorConfiguration().setTargetProject( formatTargetPath(targetProject));
        }else {
            getContext().getJavaModelGeneratorConfiguration().setTargetProject(formatTargetPath(model) + "/" + formatTargetPath(targetProject));
        }

    }

    public static void setJavaMapperConfig(String model, String targetPackage, String targetProject) {
        getContext().getJavaClientGeneratorConfiguration().setTargetPackage(targetPackage);
        if(StringUtils.isBlank(model)) {
            getContext().getJavaClientGeneratorConfiguration().setTargetProject( formatTargetPath(targetProject));
        }else {
            getContext().getJavaClientGeneratorConfiguration().setTargetProject(formatTargetPath(model) + "/" + formatTargetPath(targetProject));
        }
    }

    public static void setXMLMapperConfig(String model, String targetPackage, String targetProject) {
        getContext().getSqlMapGeneratorConfiguration().setTargetPackage(formatTargetPath(targetPackage));
        if(StringUtils.isBlank(model)) {
            getContext().getSqlMapGeneratorConfiguration().setTargetProject( formatTargetPath(targetProject));
        }else {
            getContext().getSqlMapGeneratorConfiguration().setTargetProject(formatTargetPath(model) + "/" + formatTargetPath(targetProject));
        }
    }


    public static String formatTargetPath(String path) {
        if(path.startsWith("/")) return path.substring(1);  //todo linux 下需要注释
        return path;
    }


    public static void testConnect() throws Throwable {
        Class.forName(getContext().getJdbcConnectionConfiguration().getDriverClass());
        DriverManager.getConnection(getContext().getJdbcConnectionConfiguration().getConnectionURL(), getContext().getJdbcConnectionConfiguration().getUserId(), getContext().getJdbcConnectionConfiguration().getPassword());

    }

    public static List<TableInfo> getTableInfo() throws Throwable{

        List<TableInfo> tableInfos = new ArrayList<>();
        Connection conn;
        Statement stmt;
        Class.forName(getContext().getJdbcConnectionConfiguration().getDriverClass());
        conn = DriverManager.getConnection(
                getContext().getJdbcConnectionConfiguration().getConnectionURL(),
                getContext().getJdbcConnectionConfiguration().getUserId(),
                getContext().getJdbcConnectionConfiguration().getPassword());
        stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery(getTableSql());
        while (rs.next()) {
            String tableName = rs.getString("table_name");
            String createTime = rs.getString("create_time");
            String engine = rs.getString("engine");
            String coding = rs.getString("table_collation");
            String remark = rs.getString("table_comment");
            tableInfos.add(new TableInfo(tableName,createTime,engine,coding,remark));
        }
        cacheTables = tableInfos;
        stmt.close();
        conn.close();
        return tableInfos;
    }

    public static void setConfiguration(Configuration configuration) {
        GenUtil.configuration = configuration;
    }

    public static Context getContext() {
        List<Context> contexts = configuration.getContexts();
        return contexts.stream().findAny().get();
    }

    public static String getTableSql() {
        return "select table_name ,create_time , engine, table_collation, table_comment from information_schema.tables\n" +
                "where table_schema = (select database())";
    }

    public static class TableInfo {

        /** 表名称 **/
        private String tableName;

        /** 创建日期 **/
        private String createTime;

        // 数据库引擎
        private String engine;

        // 编码集
        private String coding;

        // 备注
        private String remark;


        public TableInfo(String tableName, String createTime, String engine, String coding, String remark) {
            this.tableName = tableName;
            this.createTime = createTime;
            this.engine = engine;
            this.coding = coding;
            this.remark = remark;
        }

        public String getTableName() {
            return tableName;
        }

        public void setTableName(String tableName) {
            this.tableName = tableName;
        }

        public String getCreateTime() {
            return createTime;
        }

        public void setCreateTime(String createTime) {
            this.createTime = createTime;
        }

        public String getEngine() {
            return engine;
        }

        public void setEngine(String engine) {
            this.engine = engine;
        }

        public String getCoding() {
            return coding;
        }

        public void setCoding(String coding) {
            this.coding = coding;
        }

        public String getRemark() {
            return remark;
        }

        public void setRemark(String remark) {
            this.remark = remark;
        }
    }


}
